﻿/*******************************************************************************************************************************************
Modification History:
********************************************************************************************************************************************
Date		Author		    Description
********************************************************************************************************************************************
04/26/2011	Brian Kuhn	    Created AsyncResult<T> class
*******************************************************************************************************************************************/
using System;
using System.Threading;

namespace Concise
{
    /// <summary>
    /// Represents the status of an asynchronous operation that returns a result of type <typeparamref name="T"/>.
    /// </summary>
    /// <typeparam name="T">The type of result returned by the asynchronous operation.</typeparam>
    /// <remarks>
    /// <para>
    /// When implementing the Async Pattern, you can create and return an instance of <see cref="AsyncResult{T}"/> 
    /// and return it from your Begin<i>X</i> method. When your asynchronous operation completes, call the 
    /// <see cref="Complete(T, Boolean)"/> method, which will store the result, release the 
    /// <see cref="AsyncWaitHandle"/> and invoke any callback supplied by the client.
    /// </para>
    /// <para>
    /// To implement the End<i>X</i> method, cast the incoming <see cref="IAsyncResult"/> to 
    /// <see cref="AsyncResult{T}"/>, call <see cref="WaitHandle.WaitOne()"/> on its <see cref="AsyncWaitHandle"/> 
    /// and return its <see cref="Result"/> property.
    /// </para>
    /// </remarks>
    public class AsyncResult<T> : IAsyncResult, IDisposable
    {
        //=======================================================================================================
        //	Constructors
        //=======================================================================================================
        #region AsyncResult(AsyncCallback callback, object state)
        /// <summary>
        /// Initializes a new instance of the <see cref="AsyncResult{T}"/> class 
        /// using the specified <paramref name="callback"/> and associated <paramref name="state"/>.
        /// </summary>
        /// <param name="callback">The method to be called when a corresponding asynchronous operation is completed.</param>
        /// <param name="state">An object containing state information for this asynchronous operation.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="callback"/> is a <see langword="null"/> reference (Nothing in Visual Basic).</exception>
        public AsyncResult(AsyncCallback callback, object state) : this(callback, state, false)
        {
            
        }
        #endregion

        #region AsyncResult(AsyncCallback callback, object state, bool isCompleted)
        /// <summary>
        /// Initializes a new instance of the <see cref="AsyncResult{T}"/> class 
        /// using the specified <paramref name="callback"/> and associated <paramref name="state"/>.
        /// </summary>
        /// <param name="callback">The method to be called when a corresponding asynchronous operation is completed.</param>
        /// <param name="state">An object containing state information for this asynchronous operation.</param>
        /// <param name="isCompleted">A value that indicates whether the asynchronous operation has completed.</param>
        public AsyncResult(AsyncCallback callback, object state, bool isCompleted)
        {
            _asyncResultCallback                = callback;
            _asyncResultState                   = state;
            _asyncResultIsCompleted             = isCompleted;
            _asyncResultCompletedSynchronously  = isCompleted;

            _asyncResultWaitHandle              = new ManualResetEvent(false);
            _asyncResultSyncRoot                = new Object();
        }
        private readonly AsyncCallback _asyncResultCallback;
        private readonly object _asyncResultState;
        private readonly object _asyncResultSyncRoot;
        private readonly ManualResetEvent _asyncResultWaitHandle;
        #endregion

        //=======================================================================================================
        //	Public Properties
        //=======================================================================================================
        #region Error
        /// <summary>
        /// Gets the exception that caused the asynchronous operation to be terminated.
        /// </summary>
        /// <value>
        /// The <see cref="Exception"/> that caused the asynchronous operation to be terminated. 
        /// The default value is a <see langword="null"/> reference (Nothing in Visual Basic).
        /// </value>
        public Exception Error
        {
            get
            {
                lock (_asyncResultSyncRoot)
                {
                    return _asyncResultError;
                }
            }
        }
        private Exception _asyncResultError;
        #endregion

        #region Result
        /// <summary>
        /// Gets the result of the asynchronous operation.
        /// </summary>
        /// <value>
        /// The result of the asynchronous operation as type of <typeparamref name="T"/>.
        /// </value>
        public T Result
        {
            get
            {
                lock (_asyncResultSyncRoot)
                {
                    return _asyncResultOfOperation;
                }
            }
        }
        private T _asyncResultOfOperation;
        #endregion

        //=======================================================================================================
        //	Public Methods
        //=======================================================================================================
        #region Complete(T result, bool completedSynchronously)
        /// <summary>
        /// Completes the asynchronous operation.
        /// </summary>
        /// <param name="result">The result of the asynchronous operation to store.</param>
        /// <param name="completedSynchronously">A value that indicates whether the asynchronous operation completed synchronously.</param>
        public void Complete(T result, bool completedSynchronously)
        {
            lock (_asyncResultSyncRoot)
            {
                _asyncResultIsCompleted             = true;
                _asyncResultCompletedSynchronously  = completedSynchronously;
                _asyncResultOfOperation             = result;
            }

            this.SignalCompletion();
        }
        #endregion

        #region Complete(Exception error, bool completedSynchronously)
        /// <summary>
        /// Completes the asynchronous operation when an error occurred during the invocation of 
        /// the asynchronous operation.
        /// </summary>
        /// <param name="error">The exception that caused the asynchronous operation to be terminated.</param>
        /// <param name="completedSynchronously">A value that indicates whether the asynchronous operation completed synchronously.</param>
        public void Complete(Exception error, bool completedSynchronously)
        {
            lock (_asyncResultSyncRoot)
            {
                _asyncResultIsCompleted             = true;
                _asyncResultCompletedSynchronously  = completedSynchronously;
                _asyncResultError                   = error;
            }

            this.SignalCompletion();
        }
        #endregion

        //=======================================================================================================
        //	Private Methods
        //=======================================================================================================
        #region InvokeCallback(object state)
        /// <summary>
        /// Invokes the method to be called when the asynchronous operation has completed.
        /// </summary>
        /// <param name="state">An object containing state information for this asynchronous operation.</param>
        private void InvokeCallback(object state)
        {
            if(_asyncResultCallback != null)
            {
                _asyncResultCallback(this);
            }
        }
        #endregion

        #region SignalCompletion()
        /// <summary>
        /// Signals the <see cref="AsyncWaitHandle"/> that the asynchronous operation has completed.
        /// </summary>
        private void SignalCompletion()
        {
            _asyncResultWaitHandle.Set();

            ThreadPool.QueueUserWorkItem(new WaitCallback(this.InvokeCallback));
        }
        #endregion

        //=======================================================================================================
        //  IAsyncResult Implementation
        //=======================================================================================================
        #region AsyncState
        /// <summary>
        /// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
        /// </summary>
        /// <value>
        /// A user-defined object that qualifies or contains information about an asynchronous operation. 
        /// The default value is a <see langword="null"/> reference (Nothing in Visual Basic).
        /// </value>
        /// <remarks>
        /// This property returns the object that is the last parameter of the method that initiates 
        /// an asynchronous operation.
        /// </remarks>
        public object AsyncState
        {
            get
            {
                return _asyncResultState;
            }
        }
        #endregion

        #region AsyncWaitHandle
        /// <summary>
        /// Gets a <see cref="WaitHandle"/> that is used to wait for an asynchronous operation to complete.
        /// </summary>
        /// <value>
        /// A wait handle that is used to wait for an asynchronous operation to complete.
        /// </value>
        /// <remarks>
        /// The return value allows the client to wait for an asynchronous operation to complete instead of 
        /// polling <see cref="IsCompleted"/> until the operation concludes. The return value can be used to 
        /// perform a <see cref="WaitHandle.WaitOne()"/>, <see cref="WaitHandle.WaitAny(WaitHandle[])"/>, 
        /// or <see cref="WaitHandle.WaitAll(WaitHandle[])"/> operation.
        /// </remarks>
        public WaitHandle AsyncWaitHandle
        {
            get
            {
                return _asyncResultWaitHandle;
            }
        }
        #endregion

        #region CompletedSynchronously
        /// <summary>
        /// Gets a value that indicates whether the asynchronous operation completed synchronously.
        /// </summary>
        /// <value>
        /// <see langword="true"/> if the asynchronous operation completed synchronously; otherwise, <see langword="false"/>.
        /// </value>
        /// <remarks>
        /// If the synchronous completion of the call is detected in the <see cref="AsyncCallback"/> delegate, 
        /// it is probable that the thread that initiated the asynchronous operation is the current thread.
        /// </remarks>
        public bool CompletedSynchronously
        {
            get
            {
                lock(_asyncResultSyncRoot)
                {
                    return _asyncResultCompletedSynchronously;
                }
            }
        }
        private bool _asyncResultCompletedSynchronously;
        #endregion

        #region IsCompleted
        /// <summary>
        /// Gets a value that indicates whether the asynchronous operation has completed.
        /// </summary>
        /// <value>
        /// <see langword="true"/> if the operation is complete; otherwise, <see langword="false"/>.
        /// </value>
        /// <remarks>
        /// When this property is true, you can assume that it is safe to discard any resources 
        /// you allocated for use by the asynchronous operation.
        /// </remarks>
        public bool IsCompleted
        {
            get
            {
                lock (_asyncResultSyncRoot)
                {
                    return _asyncResultIsCompleted;
                }
            }
        }
        private bool _asyncResultIsCompleted;
        #endregion

        //=======================================================================================================
        //  IDisposable Implementation
        //=======================================================================================================
        #region Dispose()
        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion

        #region Dispose(bool disposing)
        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <param name="disposing">
        /// A value indicating if managed resources should be disposed of.
        /// </param>
        protected virtual void Dispose(bool disposing)
        {
            lock (_asyncResultDisposalLock)
            {
                if (!_asyncResultIsDisposed)
                {
                    _asyncResultIsDisposed = true;

                    this.OnDispose(disposing);
                }
            }
        }
        private readonly object _asyncResultDisposalLock = new Object();
        private bool _asyncResultIsDisposed;
        #endregion

        #region OnDispose(bool disposing)
        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <param name="disposing">
        /// A value indicating if managed resources should be disposed of.
        /// </param>
        protected virtual void OnDispose(bool disposing)
        {
            if (disposing)
            {
                lock (_asyncResultSyncRoot)
                {
                    if(_asyncResultWaitHandle != null)
                    {
                        _asyncResultWaitHandle.Dispose();
                    }
                }
            }
        }
        #endregion
    }
}
